<template>
  <div>
    <div v-show="!isError" ref="chartRef" class="custom-chart"></div>
    <el-empty v-if="isError && !isDataScreen" :description="$t('common.base.noData')" style="height: 100%; width: 100%" />
  </div>
</template>

<script>
/* vue */
import { ref, watch, onMounted, onBeforeUnmount, nextTick, computed, defineComponent } from 'vue';
/* components */
import * as echarts from 'echarts';
/* util */
import { merge, debounce } from 'lodash';
import { isOpenData } from '@src/util/platform';
import NP from 'number-precision';

export default defineComponent({
  props: {
    // 绘图数据
    data: {
      type: Array,
      default: () => [],
    },
    dataConfig: {
      type: Object,
      default: () => ({}),
    },
    // 图表类型
    chartType: {
      type: String,
      default: () => '',
    },
    // 重绘, 每次变动触发重绘
    redraw: {
      type: [Number, String],
      default: () => 0,
    },
    // 重新布局, 每次变动触发重新布局
    doLayout: {
      type: Number,
      default: () => 0,
    },
    // 图形是否可点击
    canClick: {
      type: Boolean,
      default: false,
    },
    // 在大屏内当前主题
    currentTheme: {
      type: Object,
      default: () => ({}),
    },
    // 是否是数据大屏
    isDataScreen: {
      type: Boolean,
      default: false,
    },
  },
  setup(props, { emit }) {
    // 是否进行边界检查，默认开启
    NP.enableBoundaryChecking(false);
    const chartRef = ref();
    const isError = ref(false);
    let ex = null;
    let percentage = 0;

    // 处理企微人员
    const handleStaffData = async data => {
      if (!isOpenData) {
        return;
      }

      if (window.WWOpenData?.initCanvas) {
        window.WWOpenData.initCanvas();
        window.WWOpenData.enableCanvasSharing();
      }

      const promises = [];

      for (const i of data) {
        const list = i.data;
        for (let index = 0; index < list.length; index++) {
          if (!list[index].staffId) continue;
          let items = [{ type: 'userName', id: list[index].staffId }];
          const result = await new Promise((resolve, reject) => {
            window.WWOpenData.prefetch({ items }, (err, data) => {
              if (err) {
                return reject(err);
              }
              resolve(data);
            });
          });
          try {
            list[index] = Object.assign(list[index], { displayName: result.items[0]?.data });
          } catch (error) {
            console.warn(error);
          }
        }
        promises.push(list);
      }

      return Promise.all(promises.flat());
    };

    // 生成携带的维度指标数据
    const createField = field => {
      const field_ = {};
      for (const k in field) {
        k !== 'data' && (field_[k] = field[k]);
      }
      return field_;
    };

    // 处理数据
    const filterData = async () => {
      try {
        await handleStaffData(props.data);
      } catch (err) {
        console.error(err);
      }

      const data = {
        dims: [],
        indexs: [],
        aXios: new Set(),
        aXiosObj: {},
        aXiosOrder: {},
        yAxisL: false,
        yAxisR: false,
      };
      const dataObj = {};
      props.data.forEach(i => {
        dataObj[i.name] = i;
      });
      props.dataConfig?.dimensions?.forEach(i => data.dims.push(dataObj[i.displayName]));
      props.dataConfig?.indexs?.forEach(i => {
        dataObj[i.indexName].yAxisIndex = i.axisName === 'Y2' ? 1 : 0;
        i.axisName === 'Y1' && (data.yAxisL = true);
        i.axisName === 'Y2' && (data.yAxisR = true);
        data.indexs.push(dataObj[i.indexName]);
      });
      props.dataConfig?.fxIndexs?.forEach(i => {
        dataObj[i.fxIndexName].yAxisIndex = i.axisName === 'Y2' ? 1 : 0;
        i.axisName === 'Y1' && (data.yAxisL = true);
        i.axisName === 'Y2' && (data.yAxisR = true);
        data.indexs.push(dataObj[i.fxIndexName]);
      });
      data.dims[0] &&
        data.dims[0].data.forEach(i => {
          data.aXios.add(i.key);
          data.aXiosObj[i.key] = i.displayName;
        });
      data.aXios = Array.from(data.aXios);
      data.aXios.forEach((i, j) => (data.aXiosOrder[i] = j));
      // 目标值
      if (props.dataConfig.openCustomData) {
        data.customDataType = props.dataConfig.customDataType || 'single';
        data.customDataConfig = props.dataConfig.customDataConfig || [];
      }
      return data;
    };

    // 柱形图
    const bar = filterData => {
      const seriesObj = {};
      const series = [];
      filterData.dims[0].data.forEach((i, ii) => {
        let serieItemName = '';
        const dim = [{ field_: createField(filterData.dims[0]), value_: i, isAxisX: true }];
        filterData.dims.forEach((j, ji) => {
          if (+ji > 0) {
            serieItemName += serieItemName ? `,${j.data[ii].displayName || '空'}` : `${j.data[ii].displayName || '空'}`;
            dim.push({ field_: createField(j), value_: j.data[ii], isAxisX: false });
          }
        });
        filterData.indexs.forEach(j => {
          const curSerieItemName = serieItemName ? `${serieItemName},${j.name}` : j.name;
          if (!seriesObj[curSerieItemName]) {
            seriesObj[curSerieItemName] = {
              name: curSerieItemName,
              stack: j.name,
              type: 'bar',
              yAxisIndex: j.yAxisIndex,
              data: new Array(filterData.aXios.length).fill(''),
            };
            // 大屏样式
            if (props.isDataScreen) {
              seriesObj[curSerieItemName].itemStyle = {
                borderRadius: 8,
              };
              seriesObj[curSerieItemName].itemStyle.barMinWidth = '20px';
            }
          }
          seriesObj[curSerieItemName].data[filterData.aXiosOrder[i.key]] = {
            value: j.data[ii].displayName,
            dim: [...dim, { field_: createField(j), value_: j.data[ii], isAxisX: false }],
          };
        });
      });
      for (const i in seriesObj) {
        seriesObj[i].data.forEach((j, ji) => {
          !j && (seriesObj[i].data[ji] = { value: '', dim: [] }); // 空值填充
        });
        series.push(seriesObj[i]);
      }
      // 目标值
      if (filterData.customDataType === 'single') {
        for (let index = 0; index < filterData.customDataConfig.length; index++) {
          const item = filterData.customDataConfig[index];
          series.push({
            name: item.indexName + '目标值',
            symbol: 'circle',
            type: 'line',
            smooth: true,
            itemStyle: {
              normal: {
                lineStyle: {
                  width: 0.1,
                  type: 'dashed',
                },
              },
            },
            data: Array(filterData.aXios.length).fill(parseFloat(item.customValue)),
            // 使用标线
            markLine: {
              silent: true,
              symbol: 'none',
              lineStyle: {
                width: 2,
                type: 'dashed',
                shadowColor: 'rgba(255, 255, 255, 1)',
                shadowBlur: 2,
              },
              label: {
                position: 'insideStartTop',
                color: 'inherit',
              },
              data: [{ yAxis: item.customValue }],
            },
          });
        }
      } else if (filterData.customDataType === 'many') {
        // 多目标值
        for (let index = 0; index < filterData.customDataConfig.length; index++) {
          const item = filterData.customDataConfig[index];
          const customValues = item.customValues.map(customValue => {
            const value = Object.values(customValue)[0] || 0;
            return parseFloat(value);
          });
          series.push({
            name: item.indexName + '目标值',
            symbol: 'circle',
            showSymbol: true,
            type: 'line',
            smooth: true,
            itemStyle: {
              normal: {
                lineStyle: {
                  width: 2,
                  type: 'dashed',
                  shadowColor: 'rgba(255, 255, 255, 1)',
                  shadowBlur: 2,
                },
              },
            },
            data: customValues,
          });
        }
      }
      return series;
    };

    const chartFn = {
      bar: bar,
    };

    // 生成 echarts 配置
    const chartOption = async () => {
      const color = ['#5B8FF9', '#F6903D', '#7262FD', '#F08BB4', '#61DDAA', '#F6BD16', '#65789B', '#D3CEFD', '#008685', '#A9C5FF', '#FBE5A2', '#5F94FF', '#9661BC', '#BFA6EB', '#BDEFDB', '#FCCAA1', '#5DC6F1', '#58BBBB', '#C2C8D5', '#FFD6E7'];
      const data = await filterData();
      // 初始化 option
      const option = {
        title: {},
        color: color,
        legend: {
          show: props.dataConfig.choiceLegend,
          type: 'scroll',
        },
        tooltip: {
          confine: true,
          show: true,
          formatter: function (params) {
            let result = params.data.dim?.reduce((pre, cur) => (pre += `${cur.field_.name}: ${cur.value_.displayName}${cur.dataType === 'percent' ? '%' : ''}<br />`), '');
            if (params.seriesType === 'pie') {
              if (data.customDataType === 'single') {
                if (params.data.targetValue && params.data.targetValue != 0) {
                  const percent = NP.times(NP.round(NP.divide(parseFloat(params.data.value), parseFloat(params.data.targetValue)), 4), 100) + '%';
                  result += `目标完成: ${percent}`;
                } else {
                  result += `目标完成: -`;
                }
              } else if (data.customDataType === 'many') {
                // 多目标值
                if (params.data.targetValue && params.data.targetValue != 0) {
                  const percent = NP.times(NP.round(NP.divide(parseFloat(params.data.value), parseFloat(params.data.targetValue)), 4), 100) + '%';
                  result += `目标完成: ${percent}`;
                } else {
                  result += `目标完成: -`;
                }
              }
            }
            return result;
          },
        },
        dataZoom: [{ start: 0, end: 100 }],
        series: [],
        yAxis: [
          { position: 'left', type: 'value', show: false, name: props.dataConfig.yAxisName1 },
          { position: 'right', type: 'value', show: false, name: props.dataConfig.yAxisName2 },
        ],
        xAxis: [{ type: 'category', name: props.dataConfig.xAxisName, data: [] }],
        animationDelay: 300,
        grid: {
          left: '7%',
          right: '7%',
          top: '12%',
          bottom: '60px',
          containLabel: false,
        },
      };

      // if (props.chartType !== 'pie') {
      //   option.tooltip = {
      //     show: true,
      //     trigger: 'item',
      //     formatter: '{a} <br/>{b} : {c}',
      //   };
      // }
      option.series = chartFn[props.chartType](data);
      option.xAxis[0].data = data.aXios.map(i => data.aXiosObj[i]);
      option.yAxis[0].show = data.yAxisL;
      option.yAxis[1].show = data.yAxisR;

      if (props.isDataScreen) {
        option.title.text = props.dataConfig?.chartName || '';

        option.xAxis[0].axisLine = { show: false };
        option.xAxis[0].axisTick = { show: false };

        option.yAxis[0].show = false;
        option.yAxis[1].show = false;
      }

      option.series.forEach(i => {
        if (props.chartType !== 'pie') {
          i.label = {
            show: props.dataConfig?.choiceNumber || false,
            position: 'top',
            formatter: params => {
              if (params.data.dim && params.data.dim.length) {
                return params.data.dim?.[params.data.dim.length - 1].dataType === 'percent' ? `${params.value}%` : `${params.value}`;
              } else {
                return '';
              }
            },
          };
        }

        if (props.chartType === 'pie') {
          i.label = {
            formatter: params => {
              const label = [];
              params.data.dim.forEach((i, j) => {
                if (props.dataConfig?.choiceNumber) {
                  label.push(i.value_.displayName);
                } else if (+j !== params.data.dim.length - 1) {
                  label.push(i.value_.displayName);
                }
              });
              if (params.data.dim && params.data.dim.length) {
                return params.data.dim[params.data.dim.length - 1].dataType === 'percent' ? `${label.join(',')}%` : `${label.join(',')}`;
              } else {
                return label.join(',');
              }
            },
          };
        }

        if (props.isDataScreen) {
          i.label.color = props.currentTheme?.color || '#fff';
        }
      });

      if (props.chartType === 'pie') {
        option.dataZoom[0].show = false;
        option.xAxis.forEach(i => (i.show = false));
        if (option.legend.show) {
          // 防止标签过多遮挡图例
          option.series.forEach(i => {
            i.top = '30';
          });
        }
      }

      if (props.isDataScreen) {
        // 柱状图超出12条数据轮播图形式展示
        if (isBarChartScroll.value) {
          const dataLength = props.data[0]?.data?.length;
          const indexsLength = props.dataConfig?.indexs?.length;
          percentage = Math.floor(100 / Math.ceil(dataLength / isScrollChart.bar) / indexsLength);

          option.dataZoom[0] = {
            xAxisIndex: 0,
            show: false,
            start: 0,
            end: percentage,
          };
        }

        option.legend.show = false;

        return Promise.resolve(merge(initOptions(props.currentTheme), option));
      }

      return Promise.resolve(option);
    };

    // 下载图片
    const download = name => {
      const src = ex.getDataURL({
        pixelRatio: 2,
        backgroundColor: '#fff',
        excludeComponents: ['dataZoom'],
      });
      const aEl = document.createElement('a');
      aEl.setAttribute('href', src);
      aEl.setAttribute('download', name || '图片名称');
      aEl.setAttribute('target', '_blank');
      aEl.click();
    };

    // 图形点击事件
    const clickEvent = params => {
      emit('clickEvent', params, 'chart');
    };

    // 图例点击事件
    const legendEvent = () => {
      emit('clickLegend');
    };

    // 重新布局
    const resize = () => {
      ex && ex.resize();
    };

    // 控制检查, 不通过时不绘制图形
    const createIsError = () => {
      let isError_ = false;
      if (!props.data || !props.data.length || !ex) {
        isError_ = true;
        return isError_;
      }

      props.data.forEach(i => {
        if (!i.data || !i.data.length) {
          isError_ = true;
        } else {
          isError_ = false;
        }
      });

      if (isError_) {
        return isError_;
      }

      return isError_;
    };

    let option = {};

    // 绘制
    const draw = async () => {
      if (createIsError()) {
        isError.value = true;
        return;
      }

      isError.value = false;
      option = await chartOption();
      ex.setOption(option, {
        notMerge: true,
      });

      if (props.isDataScreen) {
        barChartScroll();
      }

      resize();
    };

    /* -------------------------- 数据大屏模块内容 start -------------------------- */
    let barScrollTime = null;
    // 图表需要滚动的数值
    const isScrollChart = {
      bar: 12,
    };

    // 柱状图是否需要滚动
    const isBarChartScroll = computed(() => {
      return props.chartType === 'bar' && props.data[0]?.data?.length > isScrollChart.bar;
    });

    // 大屏模式下初始化样式
    const initOptions = (style = {}) => {
      const color = style?.color || '#fff';
      const lineStyle = { color };

      return {
        title: {
          text: '',
          top: '15px',
          left: '15px',
          textStyle: {
            fontWeight: 'normal',
            fontSize: 20,
            color,
          },
        },
        dataZoom: [
          {
            show: false,
          },
        ],
        legend: {
          top: '15px',
          right: '15px',
          textStyle: {
            color,
            fontSize: '14px',
          },
        },
        grid: {
          left: '7%',
          right: '20px',
          top: '25%',
          bottom: '30px',
          containLabel: false,
        },
        xAxis: [
          {
            axisLine: {
              lineStyle,
            },
          },
        ],
        yAxis: [
          {
            axisLine: {
              show: false,
              lineStyle,
            },
          },
          {
            axisLine: {
              show: false,
              lineStyle,
            },
          },
        ],
      };
    };

    // 多柱状图数据滚动展示
    function barChartScroll() {
      if (isBarChartScroll.value) {
        barScrollTime = setInterval(() => {
          if (!option.dataZoom && props.chartType !== 'bar') return;
          // 每次向后滚动一个，最后一个从头开始。
          if (option.dataZoom[0].end >= 100) {
            option.dataZoom[0].start = 0;
            option.dataZoom[0].end = percentage;
          } else {
            option.dataZoom[0].start = option.dataZoom[0].end;
            option.dataZoom[0].end += percentage;
          }
          ex.setOption(option);
        }, 3000);
      }
    }
    /* -------------------------- 数据大屏模块内容 end -------------------------- */

    watch(() => props.redraw, draw);

    watch(
      () => isError.value,
      () => {
        if (!isError.value) {
          nextTick(resize);
        }
      }
    );

    watch(
      () => props.doLayout,
      () => {
        resize();
        console.log('重新布局');
      }
    );

    onMounted(() => {
      if (!chartRef.value) {
        return;
      }

      ex = echarts.init(chartRef.value);

      if (props.canClick) {
        ex.on('click', clickEvent);
        ex.on('legendselectchanged', legendEvent);
      }

      if (props.data) {
        draw();
      }

      window.addEventListener('resize', resize);
    });

    onBeforeUnmount(() => {
      ex && ex.dispose();
      window.removeEventListener('resize', resize);
      barScrollTime && clearInterval(barScrollTime);
    });

    // defineExpose({
    //   // 下载图片
    //   download,
    // });
    return {
      chartRef,
      isError,
      ex,
      percentage,
      handleStaffData,
      createField,
      filterData,
      bar,
      chartFn,
      chartOption,
      download,
      clickEvent,
      legendEvent,
      resize,
      createIsError,
      option,
      draw,
      barScrollTime,
      isScrollChart,
      isBarChartScroll,
      initOptions,
      barChartScroll,
    };
  },
});
</script>

<style lang="scss" scoped>
.custom-chart {
  width: 100%;
  height: 100%;
  min-height: 276px;
}
</style>
